
#include "iec60870_master.h"
#include "iec60870_common.h"
#include "hal_time.h"
#include "hal_thread.h"
#include "hal_serial.h"
#include "cs101_slave.h"

#include <sys/time.h>
#include <glib.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include "sqlite3.h"
#include "object.h"


#define TCP_READ_MAX  255
#define ENABLE_SEND_SINGLE_POINT_TWICE 1
bool init_done = true;//false;//
static int iec101_break(void *push, int type, void *user_data, int ca)
{
  gint64 id = 0, timestamp = 0, idx = 1;
	gint rc = 0, sum_single = 0, sum_measured = 0, sum_single1 = 0,
       qos = IEC60870_QUALITY_GOOD;
  gboolean update = FALSE, is_sequence = FALSE, status = FALSE;
	float value_float = 0;
	CS101_Slave slave = NULL;
	CS101_ASDU asdu_single = NULL, asdu_measured = NULL;
	InformationObject io_single = NULL, io_measured = NULL;
#if ENABLE_SEND_SINGLE_POINT_TWICE
	CS101_ASDU asdu_single1 = NULL;
	InformationObject io_single1 = NULL;
#endif
	struct sCP56Time2a newTime;
  GObject *p = NULL, *next = NULL;
//TODO  1 2 132212 234234 1 2
  //g_debug("L%d f-%s", __LINE__, __func__);
  slave = push;
//	connection = param;
  p = user_data;
  CS101_AppLayerParameters alParameters = CS101_Slave_getAppLayerParameters(slave);
	do {
    g_object_get(p, "next", &next, "id", &id, "value", &value_float,
        "status", &status, "update", &update, "timestamp", &timestamp,
        NULL);
    CP56Time2a_createFromMsTimestamp(&newTime,
        (timestamp + 8*60*60)*1000);
    if (!update) {
	//		g_warning("point[%ld] update:%d", id, update);
      continue;
    }
    g_debug("L%d f-%s update:%"G_GINT64_FORMAT, __LINE__, __func__, id);
		if (id < 1) {
			g_warning("point id_104 is null:%"G_GINT64_FORMAT, id);
			continue;
		}
		if (!status) {
      value_float = 0;
		}
    g_object_set(p, "update", FALSE, NULL);
	//	g_debug("ca:%d id:%"G_GINT64_FORMAT, ca, id);
		if (id < 0x4001) {//single
			if (!asdu_single) {
				asdu_single = CS101_ASDU_create(alParameters, is_sequence,
            type, 0, 1, false, false);
				CS101_ASDU_setCA(asdu_single, ca);
			}
#if ENABLE_SEND_SINGLE_POINT_TWICE
      if (!asdu_single1) {
        asdu_single1 = CS101_ASDU_create(alParameters, is_sequence,
            type, 0, 1, false, false);
				CS101_ASDU_setCA(asdu_single1, ca);
      }
      io_single1 =(InformationObject) SinglePointInformation_create(
         (SinglePointInformation) io_single1, id,	(bool)value_float, qos);
      CS101_ASDU_addInformationObject(asdu_single1,	io_single1);
      sum_single1++;
#endif
      io_single =(InformationObject) SinglePointWithCP56Time2a_create(
          NULL, id,	(bool)value_float, qos, &newTime);
			rc = CS101_ASDU_addInformationObject(asdu_single,	io_single);
      idx++; sum_single++;
#if ENABLE_SEND_SINGLE_POINT_TWICE
			if (sum_single1 &&(sum_single1%20 == 0)) {
				InformationObject_destroy(io_single1);
        CS101_Slave_enqueueUserDataClass1(slave, asdu_single1);
        CS101_ASDU_destroy(asdu_single1);
				io_single1 = NULL; asdu_single1 = NULL;
			}
#endif
			if (sum_single &&(sum_single%20 == 0)) {
				InformationObject_destroy(io_single);
        CS101_Slave_enqueueUserDataClass1(slave, asdu_single);
        CS101_ASDU_destroy(asdu_single);
				io_single = NULL; asdu_single = NULL;
			}
		} else {//measured
#if ENABLE_SEND_SINGLE_POINT_TWICE
      if (asdu_single1) {
        CS101_Slave_enqueueUserDataClass1(slave, asdu_single1);
        CS101_ASDU_destroy(asdu_single1);
				io_single1 = NULL, asdu_single1 = NULL;
      }
#endif
      if (asdu_single) {
        CS101_Slave_enqueueUserDataClass1(slave, asdu_single);
        CS101_ASDU_destroy(asdu_single);
				io_single = NULL; asdu_single = NULL;
      }
			if (!asdu_measured) {
				asdu_measured = CS101_ASDU_create(alParameters, is_sequence,
            type, 0, 1, false, false);
				CS101_ASDU_setCA(asdu_measured, ca);
			}
      if (idx < 0x4001)
        idx = 0x4001;
      io_measured =(InformationObject) MeasuredValueShort_create(
         (MeasuredValueShort)io_measured, id, value_float, qos);
			rc = CS101_ASDU_addInformationObject(asdu_measured, io_measured);
      idx++; sum_measured++;
			if (sum_measured &&(sum_measured%20 == 0)) {
				InformationObject_destroy(io_measured);
        CS101_Slave_enqueueUserDataClass2(slave, asdu_measured);
        CS101_ASDU_destroy(asdu_measured);
				io_measured = NULL; asdu_measured = NULL;
			}
    }
	} while ((p = next));
#if ENABLE_SEND_SINGLE_POINT_TWICE
	if (io_single1) {
		InformationObject_destroy(io_single1);
	}
	if (asdu_single1) {
    CS101_Slave_enqueueUserDataClass1(slave, asdu_single1);
		CS101_ASDU_destroy(asdu_single1);
	}
#endif
	if (io_single) {
		InformationObject_destroy(io_single);
	}
	if (asdu_single) {
    CS101_Slave_enqueueUserDataClass1(slave, asdu_single);
		CS101_ASDU_destroy(asdu_single);
	}
	if (io_measured)
		InformationObject_destroy(io_measured);
	if (asdu_measured) {
    CS101_Slave_enqueueUserDataClass2(slave, asdu_measured);
		CS101_ASDU_destroy(asdu_measured);
	}
	return rc;
}
int tmp_addr_all(void *connection, int type, void *user_data)
{
  gint64 id = 0, timestamp = 0, idx = 1;
	gint rc = 0, sum_single = 0, sum_measured = 0, qos = IEC60870_QUALITY_GOOD;
  gboolean update = FALSE, is_sequence = TRUE, status = FALSE;
	float value_float = 0;
	CS101_ASDU asdu_single = NULL, asdu_measured = NULL;
	InformationObject io_single = NULL, io_measured = NULL;
	struct sCP56Time2a newTime;
  GObject *p = NULL, *next = NULL;
  GObject *interface;
  GObject *device;
  //GObject *software;
  CS101_AppLayerParameters alParameters = NULL;

  p = user_data;
  g_object_get(p, "parent", &interface, NULL);
  g_object_get(interface, "parent", &device, NULL);
  char *clientID;
  int ca;
  g_object_get(device, "clientID", &clientID, NULL);
  ca = atoi(clientID);
  free(clientID);
  alParameters = IMasterConnection_getApplicationLayerParameters(connection);
	do {
    g_object_get(p, "next", &next, "id", &id, "value", &value_float,
        "status", &status, "update", &update, "timestamp", &timestamp, NULL);
    CP56Time2a_createFromMsTimestamp(&newTime,(timestamp + 8*60*60)*1000);
		if (id < 1) {
			g_warning("point id_104 is null:%"G_GINT64_FORMAT, id);
			continue;
		}
		if (!status) {
      value_float = 0;
		}
    g_object_set(p, "update", FALSE, NULL);
	//	g_debug("ca:%d id:%"G_GINT64_FORMAT, ca, id);
		if (id < 0x4001) {//single
			if (!asdu_single) {
				asdu_single = CS101_ASDU_create(alParameters, is_sequence,
            type, 0, 1, false, false);
				CS101_ASDU_setCA(asdu_single, ca);
			}
      if (id != idx) {
        io_single =(InformationObject) SinglePointInformation_create(
           (SinglePointInformation) io_single, idx, false, qos);
        next = p;
      } else {
        io_single =(InformationObject) SinglePointInformation_create(
           (SinglePointInformation) io_single, id,	(bool)value_float, qos);
      }
			rc = CS101_ASDU_addInformationObject(asdu_single,	io_single);
      idx++; sum_single++;
			if (sum_single && (sum_single%20 == 0)) {
				InformationObject_destroy(io_single);
        IMasterConnection_sendASDU(connection, asdu_single);
        CS101_ASDU_destroy(asdu_single);
				io_single = NULL; asdu_single = NULL;
			}
		} else {//measured
      if (asdu_single) {
				InformationObject_destroy(io_single);
        IMasterConnection_sendASDU(connection, asdu_single);
        CS101_ASDU_destroy(asdu_single);
				io_single = NULL; asdu_single = NULL;
      }
			if (!asdu_measured) {
				asdu_measured = CS101_ASDU_create(alParameters, is_sequence,
            type, 0, 1, false, false);
				CS101_ASDU_setCA(asdu_measured, ca);
			}
      if (idx < 0x4001)
        idx = 0x4001;
      if ((idx != id) &&(type == CS101_COT_INTERROGATED_BY_STATION)) {
        io_measured =(InformationObject) MeasuredValueShort_create(
           (MeasuredValueShort)io_measured, idx, 0, qos);
        next = p;
      } else {
        io_measured =(InformationObject) MeasuredValueShort_create(
           (MeasuredValueShort)io_measured, id, value_float, qos);
      }
			rc = CS101_ASDU_addInformationObject(asdu_measured, io_measured);
      idx++; sum_measured++;
			if (sum_measured &&(sum_measured%20 == 0)) {
				InformationObject_destroy(io_measured);
        IMasterConnection_sendASDU(connection, asdu_measured);
        CS101_ASDU_destroy(asdu_measured);
				io_measured = NULL; asdu_measured = NULL;
			}
    }
	} while ((p = next));
	if (io_single) {
		InformationObject_destroy(io_single);
	}
	if (asdu_single) {
    IMasterConnection_sendASDU(connection, asdu_single);
		CS101_ASDU_destroy(asdu_single);
	}
	if (io_measured)
		InformationObject_destroy(io_measured);
	if (asdu_measured) {
    IMasterConnection_sendASDU(connection, asdu_measured);
		CS101_ASDU_destroy(asdu_measured);
	}
	return rc;
}
static bool
interrogationHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu, uint8_t qoi)
{
  g_print("Received interrogation for group %i\n", qoi);
  if (qoi == 20) { /* only handle station interrogation */
    IMasterConnection_sendACT_CON(connection, asdu, false);
		tmp_addr_all(connection, CS101_COT_INTERROGATED_BY_STATION, parameter);
    IMasterConnection_sendACT_TERM(connection, asdu);
  } else {
    IMasterConnection_sendACT_CON(connection, asdu, true);
  }
  return true;
}
static bool
asduHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu)
{
  if (CS101_ASDU_getTypeID(asdu) == C_SC_NA_1) {
    printf("received single command\n");
    if (CS101_ASDU_getCOT(asdu) == CS101_COT_ACTIVATION) {
      InformationObject io = CS101_ASDU_getElement(asdu, 0);
      if (io) {
        if (InformationObject_getObjectAddress(io) == 5000) {
          SingleCommand sc =(SingleCommand) io;
          printf("IOA: %i switch to %i\n", InformationObject_getObjectAddress(io),
                  SingleCommand_getState(sc));
          CS101_ASDU_setCOT(asdu, CS101_COT_ACTIVATION_CON);
        } else
          CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_IOA);
        InformationObject_destroy(io);
      } else {
        printf("ERROR: ASDU contains no information object!\n");
        return true;
      }
    } else
      CS101_ASDU_setCOT(asdu, CS101_COT_UNKNOWN_COT);
    printf("+++++++++:\n");
    IMasterConnection_sendASDU(connection, asdu);
    return true;
  }
  return false;
}

static void
resetCUHandler(void* parameter)
{
  printf("Received reset CU\n");

  CS101_Slave_flushQueues((CS101_Slave) parameter);
}
static void
send_init_done(void *parameter, int address)
{
  int ca = address;
  CS101_Slave slave =(CS101_Slave)parameter;
  CS101_AppLayerParameters alParameters = CS101_Slave_getAppLayerParameters(slave);
  ca = alParameters->originatorAddress;
  CS101_ASDU newAsdu = NULL;
  newAsdu = CS101_ASDU_create(alParameters, false,
      CS101_COT_INITIALIZED, 0, ca, false, false);
//	CS101_ASDU_setCA(newAsdu, ca);
  InformationObject io = NULL;
  io =(InformationObject) EndOfInitialization_create(NULL, IEC60870_COI_LOCAL_SWITCH_ON);
  CS101_ASDU_addInformationObject(newAsdu, io);
  CS101_Slave_enqueueUserDataClass1(slave, newAsdu);
  InformationObject_destroy(io);
  CS101_ASDU_destroy(newAsdu);

}
static void
linkLayerStateChanged(void* parameter, int address, LinkLayerState state)
{
    printf("Link layer state: ");

    switch (state) {
    case LL_STATE_IDLE:
//      send_variable_test(parameter);
      printf("IDLE\n");
      break;
    case LL_STATE_ERROR:
        printf("ERROR\n");
        break;
    case LL_STATE_BUSY:
        printf("BUSY\n");
        break;
    case LL_STATE_AVAILABLE:
        printf("AVAILABLE:%d\n", address);
        if (init_done)
          send_init_done(parameter, address);
        break;
    }
}


gboolean main_loop_quit(GIOChannel *channel, GIOCondition condition,
    gpointer user_data)
{
  g_main_loop_quit(user_data);
  return TRUE;
}
static bool
clockSyncHandler(void* parameter, IMasterConnection connection, CS101_ASDU asdu, CP56Time2a newTime)
{
  printf("Process time sync command with time ");
  //printCP56Time2a(newTime);
  printf("\n");
	uint64_t now_time = 0, new_time = 0;

	now_time = g_get_real_time();
	new_time = CP56Time2a_toMsTimestamp(newTime);
	if (labs(new_time - now_time) > 60*1000) {
    /*
    struct timeval tv;
    tv.tv_sec =(new_time/1000) - 8*60*60;
    tv.tv_usec = 0;
    int result = settimeofday(&tv, NULL);
    if (result == 0) {
        g_warning("System time set successfully.");
    } else {
        g_warning("Error setting system time.:%"G_GUINT64_FORMAT" :%s",
            new_time, strerror(errno));
    }
    */
    GDateTime *dt = NULL;
    char *time_str = NULL, buf[64];
    dt = g_date_time_new_from_unix_local(new_time/1000 - 8*60*60);
    time_str = g_date_time_format (dt, "%Y-%m-%d %H:%M:%S");
    snprintf(buf, sizeof(buf), "sudo date -s \"%s\"", time_str);
    system(buf);
    if (dt)
      g_date_time_unref(dt);
    if (time_str)
      g_free(time_str);
	}
  return true;
}

void * iec101_slave_init(const char *node, int baud, int data_bit,
    int parity, int stop_bit, int id, void *user_data)
{
  SerialPort port = NULL;
  CS101_Slave slave = NULL;

  g_debug("L%d f-%s node:%s", __LINE__, __func__, node);
  port = SerialPort_create(node, baud, data_bit, parity, stop_bit);
  //port = SerialPort_create(serialPort, 9600, 8, 'N', 1);
  slave = CS101_Slave_create(port, NULL, NULL,
      IEC60870_LINK_LAYER_BALANCED);
  if (id < 1) {
    id = 1;
  }
  CS101_Slave_setLinkLayerAddress(slave, id);
  CS101_Slave_setLinkLayerAddressOtherStation(slave, id);
  CS101_AppLayerParameters alParameters = CS101_Slave_getAppLayerParameters(slave);
  alParameters->sizeOfCA = 2;
  alParameters->sizeOfIOA = 2;
  alParameters->sizeOfCOT = 2;
  alParameters->originatorAddress = id;
  LinkLayerParameters llParameters = CS101_Slave_getLinkLayerParameters(slave);
  llParameters->timeoutForAck = 20000;//20s
  llParameters->addressLength = 2;
  llParameters->useSingleCharACK = false;
  CS101_Slave_setDIR(slave, true);
  CS101_Slave_setClockSyncHandler(slave, clockSyncHandler, user_data);
  CS101_Slave_setInterrogationHandler(slave, interrogationHandler, user_data);
  CS101_Slave_setASDUHandler(slave, asduHandler, NULL);
  CS101_Slave_setResetCUHandler(slave, resetCUHandler,(void*) slave);
  CS101_Slave_setIdleTimeout(slave, 5*60*1000);//5*60*1000 5minute
  CS101_Slave_setLinkLayerStateChanged(slave, linkLayerStateChanged,
     (void *)slave);
//  CS101_Slave_setRawMessageHandler(slave, rawMessageHandler, NULL);
  SerialPort_open(port);
//  gboolean status = FALSE;
  g_debug("L%d f-%s slave:%p", __LINE__, __func__, slave);
  return slave;
}
int64_t iec101_slave_run(void *slave, void *user_data,
    int64_t tm0, int ca)
{
  gint64 tm1 = 0;

//  g_debug("L%d f-%s\n", __LINE__, __func__);
  CS101_Slave_run(slave);
  tm1 = g_get_real_time()/(1000*1000);
  if ((tm1 < tm0) ||((tm1 - tm0) > 1)) {
    tm0 = tm1;
    iec101_break(slave, CS101_COT_SPONTANEOUS, user_data, ca);
  }
//  Thread_sleep(1000);
  return tm0;
}
void iec101_slave_unref(void *slave)
{
  if (slave)
    CS101_Slave_destroy(slave);
  /*
  if (port) {
    SerialPort_close(port);
    SerialPort_destroy(port);
  }
  */
  return;
}

